package json2csv
/*
Copyright 2012 Cecil New

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import (
    "encoding/json"
    "errors"
    "fmt"
    "sort"
)

type JSON2CSV struct {
    Records [][]string
    input []byte
    currow int
    curcol int
}

func (self *JSON2CSV) setCell(i int,j int,v string) {
    self.Records[i][j] = v
    fmt.Printf("(%v,%v) value is '%v'\n",i,j,v)
}
func (self *JSON2CSV) json_recursive_decoder(f interface{}) error {
    switch t := f.(type) {
    case map[string]interface{}:
        keys := make([]string,len(t))
        i := 0
        for k,_ := range t {
            keys[i] = k
            i++
        }
        sort.Strings(keys)
        for _,k := range keys {
            switch s := t[k].(type) {
            case bool, float64, string:
                self.setCell(self.currow,self.curcol,k)
                self.setCell(self.currow,self.curcol+1,fmt.Sprint(s))
                self.currow++
            case nil:
                self.setCell(self.currow,self.curcol,k)
                self.setCell(self.currow,self.curcol+1,"")
                self.currow++
            case []interface{}:
                self.setCell(self.currow,self.curcol,k)
                self.curcol++
                self.json_recursive_decoder(s)
                self.curcol--
            case map[string]interface{}:
                self.setCell(self.currow,self.curcol,k)
                self.curcol++
                self.json_recursive_decoder(s)
                self.curcol--
            default:
                self.json_recursive_decoder(s)
            }
        }
    case []interface{}:
        for k := range t {
            switch s := t[k].(type) {
            case bool, float64, string:
                self.setCell(self.currow,self.curcol,fmt.Sprint(s))
                self.currow++
            case nil:
                self.setCell(self.currow,self.curcol,"")
                self.currow++
            case []interface{}:
                self.json_recursive_decoder(s)
            case map[string]interface{}:
                self.json_recursive_decoder(s)
            default:
                self.json_recursive_decoder(s)
            }
        }
    default:
        return errors.New("Not valid type, must be map or array of interface")
    }
    return nil
}

/* 
    New() creates a JSON2CSV with estimated rows and columns.  If the estimate is not sufficient, it will panic with an index out of range runtime error.  The Records returned are always trimmed to fit the actual data: rows trimmed that exceed the actual and columns trimmed (per row) that are not needed.
*/
func New(b []byte, r int, c int) *JSON2CSV {
    j2c := new(JSON2CSV)
    j2c.input = b
    // initialize Columns
    j2c.Records = make([]([]string),r)
    for i := range j2c.Records {
        j2c.Records[i] = make([]string,c)
    }
    return j2c
}

/*
    Parse() takes a pointer to the JSON2CSV struct returned
    from the New() method and returns nil if no errors
    encountered and an error if any were encountered.  See wiki documentation on how the JSON maps and arrays are processed to make a table suitable for CSV output.  There is also a demo program that shows example usage.
*/
func (self *JSON2CSV) Parse() (error) {
    var f interface{}
    if err := json.Unmarshal(self.input,&f); err != nil {
        return err
    }
    if err := self.json_recursive_decoder(f); err != nil {
        return err
    }

    // trim empty rows and empty trailing columns
    // the last row should match currow
    // so create new Records that has that number of rows
    var trecords [][]string
    trecords = make([][]string,self.currow)
    //fmt.Printf("Untrimmed cap  is %v\n",cap(self.Records))
    //fmt.Printf("Untrimmed size is %v\n",self.currow)
    for i := 0; i < self.currow; i++ {
        // what is the index of last cell?
        var lastcell int
        for j := range self.Records[i] {
            if self.Records[i][j] != "" {
                lastcell = j
            }
        }
        lastcell++
        //fmt.Printf("Row %v: cap %v and trim size %v\n",
        //    i,cap(self.Records[i]),lastcell)
        trecords[i] = make([]string,lastcell)
        for j := range trecords[i] {
            trecords[i][j] = self.Records[i][j]
        }
    }
    // lastly reset self.Records
    self.Records = trecords
    //fmt.Printf("Trimmed size is %v\n", cap(self.Records))

    // take care of leading empty cells, since these
    // should be populated with repeating values from 
    // the prior rows
    for i := range self.Records {
        for j := range self.Records[i] {
            if self.Records[i][j] == "" {
                self.Records[i][j] = self.Records[i-1][j]
            } else {
                break
            }
        }
    }
    return nil
}